<template>
	<div class="sc-upload-multiple">
		<el-upload
			ref="uploader"
			list-type="picture-card"
			v-model:file-list="defaultFileList"
			:auto-upload="props.autoUpload"
			:disabled="props.disabled"
			:action="props.action"
			:name="props.name"
			:data="props.data"
			:http-request="request"
			:show-file-list="props.showFileList"
			:accept="props.accept"
			:multiple="props.multiple"
			:limit="props.limit"
			:before-upload="before"
			:before-remove="beforeRemove"
			:on-success="success"
			:on-error="error"
			:on-preview="handlePreview"
			:on-exceed="handleExceed">
			<slot>
				<el-icon><el-icon-plus /></el-icon>
			</slot>
			<template #tip>
				<div v-if="props.tip" class="el-upload__tip">{{ props.tip }}</div>
			</template>
			<template #file="{ file }">
				<div class="sc-upload-list-item">
					<el-image
						class="el-upload-list__item-thumbnail"
						:src="file.url"
						fit="cover"
						:preview-src-list="preview"
						:initial-index="preview.findIndex((n: any) => n == file.url)"
						hide-on-click-modal
						append-to-body
						:z-index="9999">
						<template #placeholder>
							<div class="sc-upload-multiple-image-slot">Loading...</div>
						</template>
					</el-image>
					<div v-if="!disabled && file.status == 'success'" class="sc-upload__item-actions">
						<span class="del" @click="handleRemove(file)">
							<el-icon><el-icon-delete /></el-icon>
						</span>
					</div>
					<div v-if="file.status == 'ready' || file.status == 'uploading'" class="sc-upload__item-progress">
						<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16" />
					</div>
				</div>
			</template>
		</el-upload>
		<span style="display: none !important"><el-input v-model="value"></el-input></span>
	</div>
</template>
<script setup lang="ts">
	import Sortable from 'sortablejs'
	const emit = defineEmits(['update:modelValue'])
	const props = defineProps({
		modelValue: { type: [String, Array], default: '' },
		tip: { type: String, default: '' },
		action: { type: String, default: '' },
		apiObj: { type: Object, default: () => {} },
		name: { type: String, default: files.filename },
		data: { type: Object, default: () => {} },
		accept: { type: String, default: 'image/gif, image/jpeg, image/png' },
		maxSize: { type: Number, default: files.maxSizeFile },
		limit: { type: Number, default: 0 },
		autoUpload: { type: Boolean, default: true },
		showFileList: { type: Boolean, default: true },
		multiple: { type: Boolean, default: true },
		disabled: { type: Boolean, default: false },
		draggable: { type: Boolean, default: false },
		onSuccess: { type: Function, default: () => true }
	})
	const value = ref<any>('')
	const defaultFileList = ref<any>([])
	const preview = computed(() => defaultFileList.value.map((v: { url: string }) => v.url))
	const uploader = ref<any>(null)
	watch(
		() => props.modelValue,
		(val) => {
			if (Array.isArray(val)) {
				if (JSON.stringify(val) != JSON.stringify(formatArr(defaultFileList.value))) {
					defaultFileList.value = val
					value.value = val
				}
			} else {
				if (val != toStr(defaultFileList.value)) {
					defaultFileList.value = toArr(val)
					value.value = val
				}
			}
		}
	)
	watch(
		() => defaultFileList.value,
		(val) => {
			emit('update:modelValue', Array.isArray(props.modelValue) ? formatArr(val) : toStr(val))
			value.value = toStr(val)
		},
		{ deep: true }
	)
	onMounted(() => {
		defaultFileList.value = Array.isArray(props.modelValue) ? props.modelValue : toArr(props.modelValue)
		value.value = props.modelValue
		if (!props.disabled && props.draggable) rowDrop()
	})
	const toArr = (str: string) => {
		var _arr: { name: string; url: string }[] = []
		var arr = str.split(',')
		arr.forEach((item) => {
			if (item) {
				var urlArr = item.split('/')
				var fileName = urlArr[urlArr.length - 1]
				_arr.push({
					name: fileName,
					url: item
				})
			}
		})
		return _arr
	}
	const toStr = (arr: any[]) => arr.map((v: { url: string }) => v.url).join(',')
	const formatArr = (arr: any[]) => {
		var _arr: { name: string; url: string }[] = []
		arr.forEach((item: { name: string; url: string }) => {
			if (item) {
				_arr.push({
					name: item.name,
					url: item.url
				})
			}
		})
		return _arr
	}
	const rowDrop = () => {
		const itemBox = uploader.value.$el.querySelector('.el-upload-list')
		Sortable.create(itemBox, {
			handle: '.el-upload-list__item',
			animation: 200,
			ghostClass: 'ghost',
			onEnd({ newIndex, oldIndex }) {
				const tableData = defaultFileList.value
				const currRow = tableData.splice(oldIndex, 1)[0]
				tableData.splice(newIndex, 0, currRow)
			}
		})
	}
	const before = (file: any) => {
		if (!['image/jpeg', 'image/png', 'image/gif'].includes(file.type)) {
			ElMessage.warning(`选择的文件类型 ${file.type} 非图像类文件`)
			return false
		}
		const maxSize = file.size / 1024 / 1024 < props.maxSize
		if (!maxSize) {
			ElMessage.warning(`上传文件大小不能超过 ${props.maxSize}MB!`)
			return false
		}
	}
	const success = (res: { code: number; data: { name: string; src: string }; message: string }, file: { name: string; url: string }) => {
		var os = props.onSuccess(res, file)
		if (os != undefined && os == false) {
			return false
		}
		var response = files.parseData(res)
		file.name = response.fileName
		file.url = response.src
	}
	const error = (err: any) => {
		ElNotification.error({
			title: '上传文件未成功',
			message: err
		})
	}
	const beforeRemove = async (uploadFile: { name: string }) => {
		try {
			await ElMessageBox.confirm(`是否移除 ${uploadFile.name} ?`, '提示', {
				type: 'warning'
			})
			return true
		} catch {
			return false
		}
	}
	const handleRemove = (file: any) => uploader.value.handleRemove(file)
	const handleExceed = () => ElMessage.warning(`当前设置最多上传 ${props.limit} 个文件，请移除后上传!`)
	const handlePreview = (uploadFile: { url: string | URL | undefined }) => window.open(uploadFile.url)
	const request = (param: any) => {
		var apiObj = props.apiObj ? props.apiObj : files.apiObj
		const data = new FormData()
		data.append(param.filename, param.file)
		for (const key in param.data) {
			data.append(key, param.data[key])
		}
		apiObj
			.post(data, {
				onUploadProgress: (e: { loaded: number; total: number }) => {
					const complete = parseInt((((e.loaded / e.total) * 100) | 0).toString(), 10)
					param.onProgress({ percent: complete })
				}
			})
			.then((res: { code: number; data: { name: string; src: string }; message: string }) => {
				var response = files.parseData(res)
				if (response.code == files.successCode) {
					param.onSuccess(res)
				} else {
					param.onError(response.msg || '未知错误')
				}
			})
			.catch((err: any) => param.onError(err))
	}
</script>
<style lang="less" scoped>
	.el-form-item.is-error .sc-upload-multiple:deep(.el-upload--picture-card) {
		border-color: var(--el-color-danger);
	}
	:deep(.el-upload-list__item) {
		transition: none;
		border-radius: 0;
	}
	.sc-upload-multiple:deep(.el-upload-list__item.el-list-leave-active) {
		position: static !important;
	}
	.sc-upload-multiple:deep(.el-upload--picture-card) {
		border-radius: 0;
	}
	.sc-upload-list-item {
		width: 100%;
		height: 100%;
		position: relative;
	}
	.sc-upload-multiple .el-image {
		display: block;
	}
	.sc-upload-multiple .el-image:deep(img) {
		-webkit-user-drag: none;
	}
	.sc-upload-multiple-image-slot {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		height: 100%;
		font-size: 12px;
	}
	.sc-upload-multiple .el-upload-list__item:hover .sc-upload__item-actions {
		display: block;
	}
	.sc-upload__item-actions {
		position: absolute;
		top: 0;
		right: 0;
		display: none;
	}
	.sc-upload__item-actions span {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 25px;
		height: 25px;
		cursor: pointer;
		color: #fff;
	}
	.sc-upload__item-actions span i {
		font-size: 12px;
	}
	.sc-upload__item-actions .del {
		background: #f56c6c;
	}
	.sc-upload__item-progress {
		position: absolute;
		width: 100%;
		height: 100%;
		top: 0;
		left: 0;
		background-color: var(--el-overlay-color-lighter);
	}
</style>
